/**
 * GONClient
 * This file handles the client for the Game of Nim
 * It uses the Java RMI middleware to communicate with the server
 * 
 * Author: James Wilkinson
 * Edge Hill University
 * December 2013
 * 2160481
 */
package Client;

import Server.GONServerInterface;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;



public class GONClient extends UnicastRemoteObject implements Runnable, GONClientInterface {
    
    private GONServerInterface mycs;
    private static boolean isClientTurn = false;
    
    private long clientInitTime = 0;
    
    // Monitor the current state of the game in the client.
    private static final int NIM_STATE_IDLE     = 0;        // Server is running but game isn't. Maybe waiting for another client
    private static final int NIM_STATE_RUNNING  = 1;        // Game is running either in single player or multiplayer
    private static int currentNimState = NIM_STATE_IDLE;
    
    /**
     * Constructor for this class - registers the client on the server
     * @param cs
     * @throws RemoteException 
     */
    public GONClient(GONServerInterface cs) throws RemoteException {
        mycs = cs;
        mycs.regClient(this);
    }
    
    /**
     * This method will print a message on the client's console based on the
     * string that is passed to the parameter. It adds a timestamp prior to sending
     * the message for the sake of clarity.
     * @param msg
     * @throws RemoteException 
     */
    public synchronized void sendMsg(final String msg) throws RemoteException {
        DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        Date date = new Date();
        System.out.println(""+ dateFormat.format(date) +": "+msg);
    }
    
    /**
     * getGameOfNimState
     * @return integer representing the current game of nim's state, being STATE_IDLE or STATE_RUNNING
     */
    public int getGameOfNimState() {
   //     System.out.println("State: "+ currentNimState);
        return currentNimState;
    }
       
    /**
     * Set's the current state of the game for monitoring in the client.
     * @param newState 
     */
    public void setGameOfNimState(int newState) {
  //      System.out.println("New state: " + newState);
        currentNimState = newState;
    }
    
        
    /**
     * This method will set the state of the client (i.e. is it this client's turn or not)
     * @param turn boolean representing whether it is this client's turn or not
     */
    public void setClientsTurn(boolean turn) throws RemoteException {
        isClientTurn = turn;
    }
   
    
    /**
     * This getter method returns true if it is currently this clients turn
     * otherwise false if it is the other clients (players) turn
     * @return boolean representing whether it is this clients turn or not
     */
    public boolean isClientsTurn(){
        return isClientTurn;
    }

    /**
     * This method is run in a separate thread to handle listening for client
     * input when it is the users turn.
     */
    public void run() {
        try {
            monitorClientInput();
        } catch (RemoteException ex) {
           
        }
    } 
    
    public void monitorClientInput() throws RemoteException
    {
    //    System.out.println("monitorClientInput");
        Scanner in = new Scanner(System.in);
        String msg; 
        while (true) {
            

            if(getGameOfNimState() == NIM_STATE_RUNNING) {
                /** First of all handle the listening of text to monitor when
                 * the client enters a number
                 */
                if(isClientsTurn() == true) {
                    System.out.println("Enter a number representing how many marbles you would like to take away.");            
                      try {                
                        msg = in.nextLine();
                        if(msg.length() > 0){
                          mycs.broadcast(msg, this);
                        }
                    } catch (Exception e) {
                        System.err.println("Problem…." +e);
                    }
                }else{
                    break;
                }
            }
        }
    }

    /**
     * This method is called 15 seconds after the client
     * is initialised. If the game is not running and is still
     * IDLE as no other clients have connected, it will
     * initialise single player.
     */
    public void monitorClientConnectionState() throws RemoteException {
        while(true)
        {
            // If the game is running there is no need to continue with this thread
            if(getGameOfNimState() == NIM_STATE_RUNNING) {
                clientInitTime = -1;
                return;
            }

            if(getGameOfNimState() == NIM_STATE_IDLE) {               
                
                // If the variable is -1 it means the game has at least once
                // ran so dont do anything
                if(clientInitTime == -1) {
                    return;
                }
                 
                // If the variable is 0 then it needs to be initialised for the first time
                if(clientInitTime == 0) {
                    clientInitTime = now();
                    continue;
                }

                // OK 15 seconds have passed and the game is still idle.
                // Start single player.
                if(now() - clientInitTime >= 15) {
                    mycs.initSinglePlayer();
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("Initialising client...");
        String url = "rmi://localhost/NimServer";
        System.out.println("Connecting to: "+url);
        try {
            GONServerInterface cs = (GONServerInterface) Naming.lookup(url);
            final GONClient client = new GONClient(cs);
            System.out.println("Connected to server!");
            
            // This thread checks when 15 seconds have passed to check to start single player.
            new Thread(
            new Runnable() {
                public void run() {
                    try {
                        client.monitorClientConnectionState();
                    } catch (RemoteException ex) {
                    }
                }
            }
            ).start();
            
        } catch (Exception e) {
            System.out.println("Client: Failed to connect to server!");
            System.out.println("Please ensure the server application is running and try again...");
            System.err.println("Error details: "+e);
        }
    }
    
    public void closeConnection() {
        System.exit(1);
    }
    
    /** 
     * This method returns a long integer representing the current timestamp
     * of the program.
     * @return 
     */
    public long now() {
        return System.currentTimeMillis() / 1000;
    }
}
